有一句相當有哲理的格言,是這麼說的:「找到自己的定位,才能知道人生的方向(If you don't know where you've come from, you don't know where you're going.)」
「定位」與「方向」是分不開的,給你一串經緯度,沒有地圖對你來說也沒有太大意義;而有了地圖,如果你不知道現在在哪裡,也沒有太大意義。
前面我們講的都是「地圖」相關的應用,例如搜尋地點、規劃路徑等等,都是針對「目的地」的用途,但是在找到目的地之前,要怎麼知道自己現在的位置,就是一件不得不面對的事情。
一般來說,導航機、手機、甚至智慧手錶裡面,經常都有 GNSS(Global Navigation Satellite System / 全球導航衛星系統)接收器,不管是美國的 GPS、俄羅斯的 GLONASS、歐盟的 Galileo、中國的 BeiDou、日本的 QZSS,都是用衛星與接收器的距離來算出位置。
但是,很多時候 GNSS 也不見得管用,例如在衛星訊號被遮蔽的時候,因為可見的衛星數量下降,會導致衛星定位的誤差變大,甚至大到不可參考的地步。例如台北市的市民大道,旁邊是高樓,上方有高架橋的狀況下,GNSS 的定位很容易漂移。或是在室內、地底下時,因為沒有 GNSS 的信號,GNSS 定位就沒辦法找到位置。
因此,我們現在每一支手機裡面,除了 GNSS 接收器之外,還會有網路定位的技術,所謂網路定位,指的就是用手機的基地台,不管是 2G/3G/4G/5G/WCDMA 等等,或是用 WiFi 無線網路熱點,甚至 Bluetooth 或 LoRa,原理也是相近的,都是測量訊號發送的點與接收的點來進行定位,因此能夠接收到愈多的訊號源,定位的正確度也會提高。
手機內建網路定位除了在沒有 GNSS 信號時可以提供定位之外,還有額外的好處:因為 GNSS 定位需要接收星曆,這樣才能知道每個衛星的位置,進而從 GNSS 的訊號得知 GNSS 衛星與接收器的距離,進而算出概略的位置,這樣的過程是需要時間的,少則數十秒,多則數分鐘,這段時間是沒辦法單純靠 GNSS 取得位置的,但是或許在過去大家習慣等待 GPS 冷開機並獲得定位,現在人手一機的狀況下,很多時候我們需要立刻知道現在的位置,這時候網路定位就相當重要,因為網路定位是收集手機能接收到的無線訊號來源,例如行動網路的基地台,以及無線網路的熱點,然後藉由特定的網路服務來算出位置,這樣就可以把初次定位(First Time To Fix / FTTF)的時間有效的縮短到數秒鐘。
然而,不同的定位方式,所獲得的精確度也大不相同,一般來說使用行動網路基地台,取得的誤差範圍可能從數百公尺到數公里,因此通常只能用來取得大概的位置;然而,如果是 WiFi 無線基地台的話,可以達到數十公尺到數公尺內的誤差範圍;但如果使用藍芽的話,則可以到數公尺至數十公分的誤差範圍,幾乎是幾步之遙。而傳統的 GPS 定位,在收訊良好的空曠地,最低可以到數公尺的誤差範圍,要取得精確度更高的結果,就要使用更複雜(成本更高)的技術了。
以上提到的技術中,藍芽的定位精度最高,但是卻有先天的缺陷:必須要有藍芽信號,而為了要有藍芽信號,則必須要先安裝許多的藍芽信標(Beacon),這也是一般場所不會具備的條件,因此目前最主流的,還是行動網路與 WiFi 無線網路為主。
HERE 目前所提供的網路定位服務,也支援剛剛所提到的三種技術:
如果使用網路 API 的方式進行定位,則可以使用行動網路與 WiFi;如果使用 HERE SDK(Android),則三種都可以使用;如果使用 HERE SDK(iOS),則只能使用藍芽定位,這跟 Android/iOS 開放的權限不同有關。
我們這次要介紹的是用網路 API 的方式定位的技術。
※ HERE Positioning 官方網站:https://developer.here.com/documentation/positioning/
HERE Positioning API 就是使用 API 的方式進行定位的技術,我們可以使用這個 API 上傳我們取得的行動網路基地台、WiFi 無線網路熱點的清單,HERE 就會算出一個大概的位置並回傳給我們。
如果您用一般的手機 APP,通常不需要煩惱這樣的問題,但如果您今天開發的是物聯網的裝置,例如使用 Raspberry Pi 開發板加上若干的感應器來測量溫度、濕度、風向等等的氣象資訊,但氣象資訊又必須與地理位置結合,HERE 網路定位就是一個相當好用的服務,因為現在的開發板大多都支援 WiFi 無線網路,您只要寫簡單的程式就可以掃描附近的 WiFi 熱點,並把這些資訊上傳到 HERE 網路定位的 API 來取得位置,這樣子您就可以省下 GPS 晶片的花費,也節省了許多開發的時間。
您可以用手中的 Windows 電腦來測試,如果您的電腦可以接收 WiFi 無線網路,那就可以進行以下的步驟。
請先開啟命令列模式,按下「Windows + R」來開啟執行對話框,接著在文字框中輸入「cmd
」,接著按下「Enter」或「OK」來開啟命令列模式。
接著請輸入以下指令,之後按下「Enter」:
netsh wlan show network bssid
按下 Enter 之後,您會看到一串文字跑過,這些就是您的電腦掃描到的 WiFi 無線網路清單。
您可以把這串清單輸出成文字檔,會更好閱讀。例如我們可以使用以下指令:
netsh wlan show network bssid > wifi_list.txt
按下「Enter」之後,並不會顯示任何的資訊在螢幕上,而是輸出到一個叫做 wifi_list.txt 的文字檔,我們打開這個文字檔試試看,例如我這邊掃描到的第一個 SSID 叫做 WeWork,下面有好幾個 BSSID,也就是每個 WiFi 無線網路基地台的 WAN MAC 位址。另外,每一個基地台的訊號強度、頻道、規格(802.11ac)、速度都完整的顯示出來了。
Interface name : Wi-Fi
There are 34 networks currently visible.
SSID 1 : WeWork
Network type : Infrastructure
Authentication : WPA2-Personal
Encryption : CCMP
BSSID 1 : 1c:3a:60:14:89:3c
Signal : 53%
Radio type : 802.11ac
Channel : 149
Basic rates (Mbps) : 24
Other rates (Mbps) : 36 48 54
BSSID 2 : 1c:3a:60:14:83:9c
Signal : 81%
Radio type : 802.11ac
Channel : 161
Basic rates (Mbps) : 24
Other rates (Mbps) : 36 48 54
BSSID 3 : 1c:3a:60:14:34:3c
Signal : 60%
Radio type : 802.11ac
Channel : 149
Basic rates (Mbps) : 24
Other rates (Mbps) : 36 48 54
BSSID 4 : 1c:3a:60:14:48:9c
Signal : 62%
Radio type : 802.11ac
Channel : 149
Basic rates (Mbps) : 24
Other rates (Mbps) : 36 48 54
BSSID 5 : 1c:3a:60:14:34:dc
Signal : 78%
Radio type : 802.11ac
Channel : 44
Basic rates (Mbps) : 24
Other rates (Mbps) : 36 48 54
掃描到的 WiFi 無線基地台數量依照不同的地點而異,如果您位在深山中,可能一個都找不到;但如果您在辦公大樓中,可能會掃描到數十個,甚至上百個!以我掃描到的範例來說,總共有 111 個基地台被找到。另外,裝置的接收器靈敏程度,或是環境中的電磁干擾程度都會影響找到的無線基地台數量。因此即使在同樣的地點,手機、平板電腦、筆記型電腦找到的數量可能都略有不同。
雖然基地台的數量愈多愈好,但以 HERE 網路定位 API 來說,基本上只要有三個以上的無線基地台就可以找到位置了。
接著我們就要使用這些資訊來進行網路定位,不過這部份會有點像是手動操作。HERE 網路定位 API 需要上傳的 JSON 格式是:
{
"wlan": [{
"mac": "1c:3a:60:54:6e:3c"
}, {
"mac": "1c:3a:60:14:83:9c"
}, {
"mac": "1c:3a:60:14:34:3c"
}, {
"mac": "1c:3a:60:14:48:9c"
}, {
"mac": "1c:3a:60:14:34:dc"
}]
}
請把上面這串 JSON 字串的「"mac"」後面的 MAC 位址改成您掃描到的結果,從 wifi_list.txt 複製貼上,我們先取其中五個就好了,當然您也可以測試多一點或少一點取得的結果是否有所不同。
接著我們打開 Postman,新增一個 POST request,然後填入以下資訊:
https://pos.ls.hereapi.com/positioning/v1/locate
然後我們切換到 Headers 分頁,新增一個 Key/Value:
Key:Content-Type
Value:application/json
接著我們切換到 Body 分頁,選擇「raw」,然後在下方的文字框貼上上面編輯完的 JSON 字串。
接著按下「Send」來送出請求,如果您送出的無線基地台資訊可以取得位置的話,就會取得類似下面的回傳結果,其中包含了 lat(緯度)、lng(經度)、accuracy(精確度)。
{
"location": {
"lat": 25.0358719,
"lng": 121.5684867,
"accuracy": 118
}
}
以上面這個結果來說,誤差範圍是 118 公尺,並不是太好的結果,如果我們增加無線基地台的數量呢?結果會不會變好?
以實測來看,把無線基地台的數量增加,確實也縮小了誤差範圍,從 118 公尺縮小到 57 公尺。
其實剛剛的步驟有點手動,這樣真的太慢了。我們一般而言可以用程式的方式進行掃描來進行剛剛的流程。
以下這個 Python 3.x 寫成的小程式支援 Windows 與 Mac OS,會進行剛剛的步驟,先取得 WiFi 無線網路掃描的結果,並且把取得的結果編寫成 HERE 網路定位需要的格式,上傳後得到結果,接著使用 HERE Map Image API 把結果(經緯度、誤差範圍)繪製成一張地圖影像並下載下來。
您只需要把 api_key 這個變數的內容換成您自己的 HERE API KEY,再來直接執行就可以了。
import json
import os
import platform
import plistlib
import re
import ssl
import subprocess
import urllib.request
from urllib.request import urlretrieve
from PIL import Image
def mia_cicular_picture(lon, lat, radius):
mia_url = 'https://image.maps.ls.hereapi.com/mia/1.6/mapview?c={},{}&u={}&w=1280&h=720&ml=cht' \
'&apiKey={}&t=3'.format(lat, lon, radius, api_key)
picture_file_name = "pos_result.jpg"
urlretrieve(mia_url, picture_file_name)
Image.open(picture_file_name).show()
api_key = '' # YOUR HERE API KEY
context = ssl._create_unverified_context()
positioning_headers = {'Content-Type': 'application/json'}
positioning_url = 'https://pos.ls.hereapi.com/positioning/v1/locate?apiKey={}'.format(api_key)
rev_geocoder_url = 'https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json?prox='
if platform.system() == 'Windows':
results = subprocess.check_output(["netsh", "wlan", "show", "network", "bssid"])
results = results.decode("ascii") # needed in python 3
result_list = results.replace('\r', '').split('\n')
mac_list = set()
for element in result_list:
if re.match('.*BSSID.*', element):
mac_list.add('{"mac": "' + (element.split(' : ')[1]) + '"}')
elif platform.system() == 'Darwin':
wifi_list = os.popen('/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s -x').read()
open('wifi_plist.txt', mode='w').write(wifi_list)
hotspots_plist = plistlib.loads(str.encode(wifi_list), fmt=plistlib.FMT_XML)
mac_list = set()
for hotspot in hotspots_plist:
ssid_str = hotspot['SSID_STR']
bssid = hotspot['BSSID']
rssi = hotspot['RSSI']
if not re.findall('\w\w:\w\w:\w\w:\w\w:\w\w:\w\w', hotspot['BSSID']):
bssid_arr = bssid.split(':')
bssid_modified_arr = []
for bssid_elem in bssid_arr:
if len(bssid_elem) == 1:
bssid_elem = '0{}'.format(bssid_elem)
bssid_modified_arr.append(bssid_elem)
bssid = ':'.join(bssid_modified_arr)
mac_list.add('{\"mac\":\"' + bssid + '\",\"powrx\":' + str(rssi) + '}') # BSSID + Powrx
data = '{"wlan":[' + ','.join(i for i in mac_list) + ']}'
print('Wifi hotspots:\n' + data)
req = urllib.request.Request(url=positioning_url, data=data.encode('ascii'), headers=positioning_headers)
json_result = json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
print('result:\n' + str(json_result))
lat = json_result['location']['lat']
lon = json_result['location']['lng']
radius = json_result['location']['accuracy']
mia_cicular_picture(lon, lat, radius)
我們來看看執行的結果,終端機會印出編寫好的 JSON 字串,並且也印出了定位結果({'location': {'lat': 25.0360984, 'lng': 121.5686241, 'accuracy': 29}}
)。
而下載下來的圖片(pos_result.jpg)看起來會像這樣,上面的綠點代表定位結果的經緯度位置,而綠色圈則是誤差範圍。
簡單的寫個小程式就可以透過 HERE 網路定位服務來取得位置,對於物聯網開發來說是非常方便且低成本的作法。但如果您的需求是透過手機無線基地台資訊來進行網路定位,也可以到官方網站參閱技術文件查詢相關的實做方法。
快速建構地圖服務(一) - 認識 HERE Studio / Data Hub
快速建構地圖服務(二) - 認識 HERE Data Hub CLI / API
快速建構地圖服務(三) - 使用 QGIS 玩轉 HERE Data Hub
快速建構地圖服務(四) - 當 Leaflet JS 遇見 Data Hub
快速建構地圖服務(五) - 整合 HERE 地點搜尋 API
快速建構地圖服務(六)- HERE Waypoints Sequence 路徑最佳排序
快速建構地圖服務(七)- 認識 HERE Routing API - 路徑規劃
快速建構地圖服務(八)- 認識 Matrix Routing
快速建構地圖服務(九)- Isoline Routing
快速建構地圖服務(十)- HERE Tour Planning 物流路徑預排與成本精算
快速建構地圖服務(十一)- HERE Route Matching GPS 軌跡分析
快速建構地圖服務(十二)- HERE Custom Locations 地圖資料倉儲與查詢
快速建構地圖服務(十三)- HERE Geofencing 地理圍籬
快速建構地圖服務(十四)- HERE Custom Routes 自建路網 + Vector Tile 向量圖磚 + Map Image API 靜態地圖
快速建構地圖服務(十五)- HERE Positioning 網路定位服務